package com.gss.stockLedger.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import org.springframework.beans.BeanUtils;

import com.gss.stockLedger.dao.QueryStockNoDaoSupportDAO;
import com.gss.stockLedger.domain.FIFOReportDTO;
import com.gss.stockLedger.domain.StockTransactionDtls;
import com.gss.stockLedger.form.ReportGenerateForm;

public class WacHybridDataProcessorThread implements Runnable {

	private final GenerateExcelThread generateExcelThread;
	private final Object[] partIdDetails;
	private final QueryStockNoDaoSupportDAO queryStockNoDaoSupportDAO;
	private final ReportGenerateForm reportForm;
	private final ThreadCounter threadCounter;
	
	public WacHybridDataProcessorThread(GenerateExcelThread excelThread,Object[] partIdDetails,QueryStockNoDaoSupportDAO dao
			,ReportGenerateForm reportGenerateForm, ThreadCounter threadCounter){
		this.generateExcelThread = excelThread;
		this.partIdDetails = partIdDetails;
		this.queryStockNoDaoSupportDAO = dao;
		this.reportForm = reportGenerateForm;
		this.threadCounter = threadCounter;
	}
	
	@Override
	public void run() {
		
		List<FIFOReportDTO> finalReportList = new ArrayList<FIFOReportDTO>();
		List<StockTransactionDtls> stockTransactionDtls = null;
		
		finalReportList = new ArrayList<FIFOReportDTO>();
	
		try {
			stockTransactionDtls= queryStockNoDaoSupportDAO.getStockTransactionsForProduct(reportForm,partIdDetails[0].toString(),partIdDetails[1].toString());
		} catch (Exception e) {
			e.printStackTrace();
			this.threadCounter.setNumberOfThreads(-1);
			return;
		}
//		System.out.println("number of stock entries to process: " + stockTransactionDtls.size());
		if(stockTransactionDtls.size() == 0){
			this.threadCounter.setNumberOfThreads(-1);
			return;
		}
		
		
		LinkedList<FIFOReportDTO> transactionQ = null;
		Stack<FIFOReportDTO> consumedStack = null;
		LinkedList<FIFOReportDTO> transactionQForReport = null;
		Date previousTransactDate = null;
		Calendar lastTransactionDate = Calendar.getInstance();
		Calendar currentTransactionDate = Calendar.getInstance();
		long prevClosingQuantity = 0;
		double consumptionRate = 0.0F;
		double effectiveConsumptionRate = 0.0F;
		
		transactionQ = new LinkedList<FIFOReportDTO>();
		transactionQForReport = new LinkedList<FIFOReportDTO>();
		consumedStack = new Stack<FIFOReportDTO>();
		
		//sorted list according to receipt date
		long activeQuantity=0;
		float activeRate=0;
		double activeValue=0.0;
		float currentRate = 0.0F;
		FIFOReportDTO activeRow ;
	
		
	
		// in the following loop, all receipts are accumulated into a linked list so that they can be later used for consumption following FIFO
		try{
			for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
				activeRow = new FIFOReportDTO();
				BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
				
				if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR")) {
					//change code to incorporate multiple grir entries on same day with same assignment number
					if(transactionQ.size()>0&&transactionQ.contains(activeRow))
					{
						int index =transactionQ.indexOf(activeRow);
						FIFOReportDTO fromQueue = transactionQ.get(index);
						fromQueue.setQuantity(fromQueue.getQuantity()+activeRow.getQuantity());
						fromQueue.setValue(fromQueue.getValue() + activeRow.getQuantity()*activeRow.getRate());
					}
					else
					{
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						temp.setValue((double) (temp.getQuantity() * temp.getRate()));
						transactionQ.add(temp);
					}
				}
			}
		
			// applying all adjustments on respective Purchases (GRIR)
		
			for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
				activeRow = new FIFOReportDTO();
				BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
				
				if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
						activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")
						||activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {
					
					for (FIFOReportDTO fifoReportDTO : transactionQ) {
						if(null != activeRow.getAssignmentId() && activeRow.getAssignmentId().equalsIgnoreCase(fifoReportDTO.getAssignmentId())){
							if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
								if(activeRow.getQuantity()!=null)
								fifoReportDTO.setQuantity(fifoReportDTO.getQuantity()+activeRow.getQuantity());
							}
							if(activeRow.getValue()!=null)
								fifoReportDTO.setValue(fifoReportDTO.getValue() + activeRow.getValue());
							
							if(fifoReportDTO.getQuantity() != 0)
								fifoReportDTO.setRate((float) (fifoReportDTO.getValue() / fifoReportDTO.getQuantity()));
							else
								fifoReportDTO.setRate(0.0f);
							
							break;
						}
					}
				}
			}
		
			
			// above loop results into a transactionQ with all receipts along with their reversals and adjustments
			// running loop on this transactionQ, to remove any entries with zero or negative quantity or value
			
							
//			List<FIFOReportDTO> toRemoveList = new ArrayList<FIFOReportDTO>();
//			for(FIFOReportDTO receipts : transactionQ){
//				if(receipts.getQuantity() <=0 || receipts.getValue() <=0){
//					//transactionQ.remove(transactionQ.indexOf(receipts));
//					toRemoveList.add(receipts);
//				}else {
//					receipts.setRate((float) (receipts.getValue() / receipts.getQuantity()));
//				}
//			}
//			if(toRemoveList.size() > 0){
//				for(FIFOReportDTO index : toRemoveList)
//					transactionQ.remove(index);
//			}
		
			// now we have transactionQ with all entries having positive Quantity and positive value and rate populated based on final values
		
			long counter=-1;
			FIFOReportDTO previousRow = null;
			for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
			
				activeRow = new FIFOReportDTO();
				BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
				counter++;
				if(counter==0)
				{
					if(activeRow.getTransactionDate().after(reportForm.getSecStartDate())){
						finalReportList.add(activeRow);
					}
					
					if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR"))
					{
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						if(transactionQ.size()>0 && transactionQ.contains(temp))
						{
							temp.setRate(transactionQ.getFirst().getRate());
							temp.setValue(transactionQ.getFirst().getValue());
							activeQuantity = transactionQ.getFirst().getQuantity();
							transactionQForReport.add(temp);
							transactionQ.removeFirst();
						}
						 consumptionRate = temp.getRate();
						 if(activeQuantity>0)
							 effectiveConsumptionRate = consumptionRate; //effectiveconsumption rate is same as consumption rate as it is first entry 
					}
					else if(activeRow.getTransactionType().equalsIgnoreCase("OPENING")){
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						//temp.setRate((float) (temp.getValue() / temp.getQuantity()));
						temp.setValue((double) (temp.getRate()*temp.getQuantity()));
						temp.setAssignmentId("OpeningGRN");
						transactionQForReport.add(temp);
						
						//change to show value as product of quantity and rate
						activeRow.setValue((double) (temp.getRate()*temp.getQuantity()));
						activeQuantity = activeRow.getQuantity()!=null ? activeRow.getQuantity() : 0;
						consumptionRate = temp.getRate();
						if(activeRow.getQuantity()>0)
							effectiveConsumptionRate = consumptionRate;  //effectiveconsumption rate is same as consumption rate as it is first entry
						//transactionQ.removeFirst();
					}else if(WacHybridReportGenerationThreadedService.Consumption_Transaction_Type.contains(activeRow.getTransactionType())){
						
						FIFOReportDTO consumedStockDTO = new FIFOReportDTO();
						consumedStockDTO.setTransactionType("ZGRIR");
						consumedStockDTO.setAssignmentId("ApplicationGenerated");
						consumedStockDTO.setQuantity(activeRow.getQuantity());
						consumedStockDTO.setValue(0.0d);
						consumedStockDTO.setRate(currentRate);
						consumedStack.push(consumedStockDTO);
						
						activeRow.setQuantity(activeRow.getQuantity() * -1);
						activeRow.setRate(0.0f);
						activeRow.setValue(0.0d);
						consumptionRate = 0.0f;
						effectiveConsumptionRate = consumptionRate;  //effectiveConsumption rate is same as consumption rate as it is first entry bot  = 0
						activeQuantity = activeRow.getQuantity();
					}
					
					activeRow.setClosingQuantity(activeQuantity);
					activeRow.setClosingRate((float) effectiveConsumptionRate);
					activeValue = activeQuantity * effectiveConsumptionRate;
					activeRow.setClosingValue(activeValue);
//					activeQuantity = activeRow.getQuantity()!=null?activeRow.getQuantity():0;
//					if(activeRow.getRate()!=null&&activeRow.getQuantity()!=null)
//						activeValue = activeRow.getRate()*activeRow.getQuantity();
//					else
//						activeValue = activeRow.getValue();
					
					continue;
				}
				
				String transactionType = stockTransactionDtls2.getTransactionType();
				
				if(transactionType.equalsIgnoreCase("ZGRIR")) //grir is purchase 
				{
	//				System.out.println("###" + activeRow.getAssignmentId() + " :::: " + effectiveConsumptionRate + " ,, " + activeQuantity + "+++ " + activeRate + "/// " + activeValue);
					FIFOReportDTO temp = new FIFOReportDTO();
					BeanUtils.copyProperties(activeRow, temp);
					if(transactionQ.size()>0 && transactionQ.contains(temp))
					{
						temp.setRate(transactionQ.getFirst().getRate());
						temp.setValue(transactionQ.getFirst().getValue());
						long quantityToUse = transactionQ.getFirst().getQuantity();
						transactionQForReport.add(temp);
						transactionQ.removeFirst();
						consumptionRate = temp.getRate();
					
					
					if(activeQuantity < 0 ) 
					{ 
						if( (activeQuantity + quantityToUse) >= 0) {
							activeQuantity+=quantityToUse;
							activeRow.setClosingValue(consumptionRate * activeQuantity);
							activeValue = consumptionRate * activeQuantity;
							activeRow.setClosingRate((float) consumptionRate);
							effectiveConsumptionRate = consumptionRate;
						}else{
							activeQuantity+=quantityToUse;
							activeRow.setClosingValue(effectiveConsumptionRate*activeQuantity);
							activeRow.setClosingRate((float) effectiveConsumptionRate);
							activeValue = activeRow.getClosingValue();
						}
					}
					else if(activeQuantity >= 0)
					{
						activeValue+=(quantityToUse*consumptionRate);
						//activeRow.setClosingValue(activeRow.getValue()!=null?activeValue+=activeRow.getValue():activeValue);
						activeRow.setClosingValue(activeValue);
						activeQuantity+=quantityToUse;
						activeRow.setClosingRate((float) (activeValue/activeQuantity));
						effectiveConsumptionRate = (float) (activeValue/activeQuantity);
	//					System.out.println("***" + activeRow.getAssignmentId() + " :::: " + effectiveConsumptionRate + " ,, " + activeRow.getQuantity() + "+++ " + activeRow.getRate() + "/// " + activeRow.getValue());
	//					System.out.println("%%%%%" + activeRow.getAssignmentId() + " :::: " + effectiveConsumptionRate + " ,, " + activeQuantity + "+++ " + activeRate + "/// " + activeValue);
					}
					
					activeRow.setClosingQuantity(activeQuantity);
					}
					else{
//						consumptionRate = activeRow.getValue()/activeRow.getQuantity();
						activeRow.setClosingQuantity(activeQuantity);
						activeRow.setClosingRate((float) effectiveConsumptionRate);
						activeRow.setClosingValue(activeValue);
					}
					
					/*if(activeRow.getClosingQuantity()!=0)
					{
						consumptionRate = activeRow.getClosingValue()/activeRow.getClosingQuantity();
						if(activeRow.getClosingQuantity()>0)
							effectiveConsumptionRate = consumptionRate;	//making effective consumption rate = consumption rate only when consumption rate >0
					}
					else
					{
						consumptionRate = 0;
						//effectiveConsumptionRate = consumptionRate;	//not changing the effective consumption rate
					}*/
					
//					System.out.println(activeRow.getAssignmentId() +  " --- " + consumptionRate);
				}
				else if(transactionType.equalsIgnoreCase("CPA"))
				{
					
				}
				else if(WacHybridReportGenerationThreadedService.Consumption_Transaction_Type.contains(transactionType)){
					
					long quantityToConsume = activeRow.getQuantity();
					//double consumedQuantityValue = consumptionRate*quantityToConsume;
					double consumedQuantityValue = effectiveConsumptionRate*quantityToConsume;//calculation value using effective conumption rate instead of consumtion rate
	
	
					activeRow.setClosingQuantity(activeQuantity-=quantityToConsume); 
					
						
					if(quantityToConsume < 0){
						activeRow.setClosingRate((float) effectiveConsumptionRate);
						activeRow.setClosingValue((double) (activeRow.getClosingQuantity() * activeRow.getClosingRate()));
						activeValue = activeRow.getClosingValue();
					}
					else{
						if(activeQuantity > 0){
							activeRow.setClosingValue(activeValue-=consumedQuantityValue);
							activeRow.setClosingRate((float) effectiveConsumptionRate);
						}
						else if(activeQuantity <=0){
							activeRow.setClosingRate((float) effectiveConsumptionRate);
							activeRow.setClosingValue((double) (activeRow.getClosingQuantity() * activeRow.getClosingRate()));
							activeValue = activeRow.getClosingValue();
						}
					}
					
					
					/*else
						activeRow.setClosingRate(0.0f);*/
					
					//activeRow.setRate((float) consumptionRate);
					//not sure about this one need to check
					activeRow.setRate((float) effectiveConsumptionRate);
					
					activeRow.setQuantity(quantityToConsume*-1);
					activeRow.setValue(consumedQuantityValue * -1);
					
					//wacReportList.add(activeRow);
				}
				
				else if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
						activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")||
						activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {

					/*if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
						if(activeRow.getQuantity()!=null)
							activeRow.setClosingQuantity(activeQuantity+activeRow.getQuantity());
						else
							activeRow.setClosingQuantity(activeQuantity);
					}
					else
						activeRow.setClosingQuantity(activeQuantity); */
					
					activeRow.setClosingQuantity(activeQuantity);
					activeRow.setClosingValue(activeValue);
					activeRow.setClosingRate((float) effectiveConsumptionRate);
					
					/*if(activeRow.getValue()!=null)
						activeRow.setClosingValue(activeValue+=activeRow.getValue());
					else
						activeRow.setClosingValue(activeValue);//to be changed
					
					activeRow.setClosingRate((float) (activeValue / activeQuantity));
					
					//change as to pick new rate after adjustments
					consumptionRate = activeRow.getClosingRate();
					if(activeRow.getClosingQuantity()>0)
						effectiveConsumptionRate = consumptionRate;*/
					activeRow.setQuantity(null);
					activeRow.setRate(null);
	//					wacReportList.add(activeRow);
				}
				
				if(activeRow.getTransactionDate().after(reportForm.getSecStartDate()))
					finalReportList.add(activeRow);
				if(finalReportList.size() == 1 && null != previousRow ){
					finalReportList.add(finalReportList.get(0));
					previousRow.setTransactionType("OPENING");
					previousRow.setQuantity(null);
					previousRow.setRate(null);
					previousRow.setValue(null);
					previousRow.setTransactionDate(reportForm.getStartDate());
					finalReportList.set(0, previousRow);
				}
				previousRow = activeRow;
				
			}
		
			//add data in thread map
			generateExcelThread.partId_data_map.put(partIdDetails[0].toString() + "####" + partIdDetails[1].toString(), finalReportList);
	//		System.out.println("size of map: " + generateExcelThread.getPartId_data_map().size());
		}
		catch (Throwable e) {
			e.printStackTrace();
		}
		finally{
			this.threadCounter.setNumberOfThreads(-1);
		}
	}

	
}
